/**
 * @file position_keeper.h
 * @brief 仓位管理器的定义
 * 
 * @details 该文件定义了仓位管理器（PositionKeeper）类和仓位信息（PositionInfo）结构体，
 * 用于跟踪和计算所有交易工具的仓位、盈亏（已实现和未实现）和交易量。
 * 这些组件在交易系统中负责维护交易账户的当前状态。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include "common/macros.h"
#include "common/types.h"
#include "common/logging.h"

#include "exchange/order_server/client_response.h"

#include "market_order_book.h"

using namespace Common;

/**
 * @namespace Trading
 * @brief 交易相关的命名空间
 * 
 * @details 包含交易系统的核心组件，如订单、仓位管理器、交易引擎等。
 */
namespace Trading {
  /**
   * @struct PositionInfo
   * @brief 单个交易工具的仓位信息结构体
   * 
   * @details 跟踪和计算单个交易工具的仓位、盈亏（已实现和未实现）和交易量。
   * 该结构体维护了交易工具的当前仓位状态，包括仓位大小、开仓均价、已实现和未实现盈亏等。
   */
  struct PositionInfo {
    /**
     * @brief 当前仓位大小
     * 
     * @details 表示交易工具的当前仓位大小，正值表示多头仓位，负值表示空头仓位，0表示无仓位。
     */
    int32_t position_ = 0;
    
    /**
     * @brief 已实现盈亏、未实现盈亏和总盈亏
     * 
     * @details real_pnl_: 已实现盈亏，表示已完成交易的盈亏。
     * unreal_pnl_: 未实现盈亏，表示当前持有仓位的浮动盈亏。
     * total_pnl_: 总盈亏，已实现盈亏和未实现盈亏的总和。
     */
    double real_pnl_ = 0, unreal_pnl_ = 0, total_pnl_ = 0;
    
    /**
     * @brief 开仓加权平均价数组
     * 
     * @details 按侧向（买入或卖出）存储的开仓加权平均价格。
     * 用于计算未实现盈亏和平仓时的已实现盈亏。
     */
    std::array<double, sideToIndex(Side::MAX) + 1> open_vwap_;
    
    /**
     * @brief 总交易量
     * 
     * @details 该交易工具的总交易量，包括所有买入和卖出的成交量。
     */
    Qty volume_ = 0;
    
    /**
     * @brief 当前最优买卖盘口指针
     * 
     * @details 指向当前交易工具的最优买卖盘口（BBO）的指针，用于计算未实现盈亏。
     */
    const BBO *bbo_ = nullptr;

    /**
     * @brief 将仓位信息转换为字符串表示
     * 
     * @details 生成仓位信息的文本表示，包括仓位大小、未实现盈亏、已实现盈亏、
     * 总盈亏、交易量、开仓均价和当前最优买卖盘口。
     * 这个方法主要用于调试和日志记录。
     * 
     * @return 仓位信息的字符串表示
     */
    auto toString() const {
      std::stringstream ss;
      ss << "Position{"
         << "pos:" << position_
         << " u-pnl:" << unreal_pnl_
         << " r-pnl:" << real_pnl_
         << " t-pnl:" << total_pnl_
         << " vol:" << qtyToString(volume_)
         << " vwaps:[" << (position_ ? open_vwap_.at(sideToIndex(Side::BUY)) / std::abs(position_) : 0)
         << "X" << (position_ ? open_vwap_.at(sideToIndex(Side::SELL)) / std::abs(position_) : 0)
         << "] "
         << (bbo_ ? bbo_->toString() : "") << "}";

      return ss.str();
    }

    /**
     * @brief 处理成交并更新仓位、盈亏和交易量
     * 
     * @details 根据成交响应更新仓位信息。该方法执行以下操作：
     * 1. 更新仓位大小和交易量
     * 2. 根据仓位变化类型（开仓、增仓、减仓或翻转）更新开仓均价
     * 3. 计算已实现盈亏（如果有平仓）
     * 4. 更新未实现盈亏和总盈亏
     * 5. 记录日志
     * 
     * @param client_response 成交响应指针，包含成交信息
     * @param logger 日志记录器指针，用于记录仓位变化
     * @return void
     */
    auto addFill(const Exchange::MEClientResponse *client_response, Logger *logger) noexcept {
      const auto old_position = position_;
      const auto side_index = sideToIndex(client_response->side_);
      const auto opp_side_index = sideToIndex(client_response->side_ == Side::BUY ? Side::SELL : Side::BUY);
      const auto side_value = sideToValue(client_response->side_);
      position_ += client_response->exec_qty_ * side_value;
      volume_ += client_response->exec_qty_;

      if (old_position * sideToValue(client_response->side_) >= 0) { // opened / increased position.
        open_vwap_[side_index] += (client_response->price_ * client_response->exec_qty_);
      } else { // decreased position.
        const auto opp_side_vwap = open_vwap_[opp_side_index] / std::abs(old_position);
        open_vwap_[opp_side_index] = opp_side_vwap * std::abs(position_);
        real_pnl_ += std::min(static_cast<int32_t>(client_response->exec_qty_), std::abs(old_position)) *
                     (opp_side_vwap - client_response->price_) * sideToValue(client_response->side_);
        if (position_ * old_position < 0) { // flipped position to opposite sign.
          open_vwap_[side_index] = (client_response->price_ * std::abs(position_));
          open_vwap_[opp_side_index] = 0;
        }
      }

      if (!position_) { // flat
        open_vwap_[sideToIndex(Side::BUY)] = open_vwap_[sideToIndex(Side::SELL)] = 0;
        unreal_pnl_ = 0;
      } else {
        if (position_ > 0)
          unreal_pnl_ =
              (client_response->price_ - open_vwap_[sideToIndex(Side::BUY)] / std::abs(position_)) *
              std::abs(position_);
        else
          unreal_pnl_ =
              (open_vwap_[sideToIndex(Side::SELL)] / std::abs(position_) - client_response->price_) *
              std::abs(position_);
      }

      total_pnl_ = unreal_pnl_ + real_pnl_;

      std::string time_str;
      logger->log("%:% %() % % %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str),
                  toString(), client_response->toString().c_str());
    }

    /**
     * @brief 处理盘口最优价格（BBO）变化，并在有持仓时更新未实现盈亏
     * 
     * @details 根据新的最优买卖盘口价格更新未实现盈亏。该方法执行以下操作：
     * 1. 更新当前最优买卖盘口指针
     * 2. 如果有持仓且买卖盘口价格有效，计算未实现盈亏
     * 3. 更新总盈亏
     * 4. 如果盈亏发生变化，记录日志
     * 
     * 注意：未实现盈亏基于买卖盘口的中间价格计算。
     * 
     * @param bbo 新的最优买卖盘口指针
     * @param logger 日志记录器指针，用于记录盈亏变化
     * @return void
     */
    auto updateBBO(const BBO *bbo, Logger *logger) noexcept {
      std::string time_str;
      bbo_ = bbo;

      if (position_ && bbo->bid_price_ != Price_INVALID && bbo->ask_price_ != Price_INVALID) {
        const auto mid_price = (bbo->bid_price_ + bbo->ask_price_) * 0.5;
        if (position_ > 0)
          unreal_pnl_ =
              (mid_price - open_vwap_[sideToIndex(Side::BUY)] / std::abs(position_)) *
              std::abs(position_);
        else
          unreal_pnl_ =
              (open_vwap_[sideToIndex(Side::SELL)] / std::abs(position_) - mid_price) *
              std::abs(position_);

        const auto old_total_pnl = total_pnl_;
        total_pnl_ = unreal_pnl_ + real_pnl_;

        if (total_pnl_ != old_total_pnl)
          logger->log("%:% %() % % %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str),
                      toString(), bbo_->toString());
      }
    }
  };

  /**
   * @class PositionKeeper
   * @brief 顶级仓位管理器类，用于计算所有交易工具的仓位、盈亏和交易量
   * 
   * @details 该类管理所有交易工具的仓位信息，包括跟踪每个交易工具的仓位、盈亏和交易量。
   * 它提供了添加成交、更新盘口价格和获取仓位信息的方法。
   * 这个类是交易系统中跟踪和管理账户状态的核心组件。
   */
  class PositionKeeper {
  public:
    /**
     * @brief 仓位管理器的构造函数
     * 
     * @details 初始化仓位管理器，设置日志记录器。
     * 初始化时所有交易工具的仓位信息都被设置为默认值。
     * 
     * @param logger 日志记录器指针，用于记录仓位变化和盈亏信息
     */
    PositionKeeper(Common::Logger *logger)
        : logger_(logger) {
    }

    /**
     * @brief 删除的默认构造函数、拷贝构造函数、移动构造函数和赋值运算符
     * 
     * @details 仓位管理器类不允许默认构造、拷贝或移动，以确保其安全使用。
     * 这些操作被显式删除，以防止意外创建多个仓位管理器实例或意外复制。
     */
    PositionKeeper() = delete;

    /**
     * @brief 删除的拷贝构造函数
     */
    PositionKeeper(const PositionKeeper &) = delete;

    /**
     * @brief 删除的移动构造函数
     */
    PositionKeeper(const PositionKeeper &&) = delete;

    /**
     * @brief 删除的拷贝赋值运算符
     */
    PositionKeeper &operator=(const PositionKeeper &) = delete;

    /**
     * @brief 删除的移动赋值运算符
     */
    PositionKeeper &operator=(const PositionKeeper &&) = delete;

  private:
    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于在日志记录中存储当前时间的字符串表示，避免重复创建。
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器指针
     * 
     * @details 用于记录仓位变化和盈亏信息的日志记录器。
     */
    Common::Logger *logger_ = nullptr;

    /**
     * @brief 从证券标识符到仓位信息的哈希映射容器
     * 
     * @details 使用固定大小的数组实现的哈希表，用于存储和管理所有交易工具的仓位信息。
     * 数组大小由 ME_MAX_TICKERS 常量决定，表示系统支持的最大证券数量。
     */
    std::array<PositionInfo, ME_MAX_TICKERS> ticker_position_;

  public:
    /**
     * @brief 处理成交并更新相应证券的仓位信息
     * 
     * @details 将成交响应转发给相应证券的仓位信息对象进行处理。
     * 该方法会更新证券的仓位、盈亏和交易量。
     * 
     * @param client_response 成交响应指针，包含成交信息和证券标识符
     * @return void
     */
    auto addFill(const Exchange::MEClientResponse *client_response) noexcept {
      ticker_position_.at(client_response->ticker_id_).addFill(client_response, logger_);
    }

    /**
     * @brief 更新指定证券的最优买卖盘口价格
     * 
     * @details 将新的最优买卖盘口价格转发给相应证券的仓位信息对象进行处理。
     * 该方法会更新证券的未实现盈亏和总盈亏。
     * 
     * @param ticker_id 证券标识符
     * @param bbo 新的最优买卖盘口指针
     * @return void
     */
    auto updateBBO(TickerId ticker_id, const BBO *bbo) noexcept {
      ticker_position_.at(ticker_id).updateBBO(bbo, logger_);
    }

    /**
     * @brief 获取指定证券的仓位信息
     * 
     * @details 返回指定证券的仓位信息对象的指针，允许调用者访问和查询该证券的仓位状态。
     * 
     * @param ticker_id 证券标识符
     * @return 指向该证券的仓位信息对象的指针
     */
    auto getPositionInfo(TickerId ticker_id) const noexcept {
      return &(ticker_position_.at(ticker_id));
    }

    /**
     * @brief 将所有证券的仓位信息转换为字符串表示
     * 
     * @details 生成所有证券的仓位信息的文本表示，包括每个证券的仓位、盈亏和交易量，
     * 以及所有证券的总盈亏和总交易量。
     * 这个方法主要用于调试和日志记录。
     * 
     * @return 所有证券的仓位信息的字符串表示
     */
    auto toString() const {
      double total_pnl = 0;
      Qty total_vol = 0;

      std::stringstream ss;
      for(TickerId i = 0; i < ticker_position_.size(); ++i) {
        ss << "TickerId:" << tickerIdToString(i) << " " << ticker_position_.at(i).toString() << "\n";

        total_pnl += ticker_position_.at(i).total_pnl_;
        total_vol += ticker_position_.at(i).volume_;
      }
      ss << "Total PnL:" << total_pnl << " Vol:" << total_vol << "\n";

      return ss.str();
    }
  };
}
